/*
 * coffee2dev.c
 * 
 * Module for coffee machine devices 
 *  
 * Copyright (C) 2010- Valentina Vuksic <v@valvuk.ch>
 * Copyright (C) 2010- Beat Michel Liechti <bml303@gmail.com> 
 *  
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 * 
 */

/* -- "regular" include files */
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <linux/cdev.h>
#include <linux/completion.h>
#include <linux/dcache.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/io.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/kfifo.h>
#include <linux/module.h>
#include <linux/path.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/timex.h>

/* -- required for architecture specific low-level gpio */
#include <mach/pxa-regs.h>
#include <mach/pxa2xx-regs.h>
#include <mach/pxa2xx-gpio.h>
#include <mach/pxa2xx_spi.h>
#include <mach/ssp.h>
#include <mach/regs-ssp.h>
#include <mach/gpio.h>

/* -- module metadata */

MODULE_LICENSE("GPL");
MODULE_AUTHOR("v@valvuk.ch,bml303@gmail.com");
MODULE_DESCRIPTION("coffee2dev");

/* -- which board? */
//#define ORCHID
#define CARME

//#define USE_DIRECT
//#define USE_TASKLET
#define USE_WORKQUEUE

/* -- defines ----------------------------------------------------------------*/

#ifdef ORCHID

/* -- GPIO for buttons */
#define GPIO_S5 12
#define GPIO_S6 11
#define GPIO_S7 17
#define GPIO_S8 16
#define GPIO_SELECTINPUT 117

/* -- GPIO for LED */
#define GPIO_V1 35
#define GPIO_V2 37
#define GPIO_V3 36
#define GPIO_V4 79
#define GPIO_V5 15
#define GPIO_V6 80
#define GPIO_V7 52
#define GPIO_V8 19
#define GPIO_SELECTOUTPUT 118

#elif defined(CARME)

/* -- leds, switches and other resources are memory mapped */
#define MAP_SIZE		(6*4096)		/*! Six pages */
#define IO_BASE			(0xc000000 )	/*! IO base in physical memory */
#define LED_OFFSET		(0x3000)		/*! Offset of led register in IO space */
#define SWITCH_OFFSET	(0x3200)		/*! Offset of switch register in IO space */

/* -- GPIO and labels for buttons */
#define GPIO_T0 99
#define GPIO_T1 101
#define GPIO_T2 102
#define GPIO_T3 103
#define LABL_T0 "T0"
#define LABL_T1 "T1"
#define LABL_T2 "T2"
#define LABL_T3 "T3"

/* -- timer related */
#define TIMER_INTERVAL (HZ / 10)

/* -- values used for buttons */
#define VAL_BTN_00_S0	'0'	/* S0 = 0x30 */
#define VAL_BTN_01_S1	'1'	/* S1 = 0x31 */
#define VAL_BTN_02_S2	'2'	/* S2 = 0x32 */
#define VAL_BTN_03_S3	'3'	/* S3 = 0x33 */
#define VAL_BTN_04_S4	'4'	/* S4 = 0x34 */
#define VAL_BTN_05_S5	'5'	/* S5 = 0x35 */
#define VAL_BTN_06_S6	'6'	/* S6 = 0x36 */
#define VAL_BTN_07_S7	'7'	/* S7 = 0x37 */
#define VAL_BTN_08_T0	'8'	/* T0 = 0x38 */
#define VAL_BTN_09_T1	'9'	/* T1 = 0x39 */
#define VAL_BTN_10_T2	':'	/* T2 = 0x3A */
#define VAL_BTN_11_T3	';'	/* T3 = 0x3B */

/* -- button kfifo related */
#define BUTTON_KFIFO_LENGTH		128

/* -- defines for PWM */

#define GPIO_PWM3_SPEED		12
#define GPIO_PWM3_DIRECTION	21
#define LABL_PWM3_SPEED		"PWM3_SPEED"
#define LABL_PWM3_DIRECTION	"PWM3_DIRECTION"

#define PWMCR3  __REG(0x40C00010) /* PWM Control Register 3 */
#define PWMDCR3 __REG(0x40C00014) /* PWM Duty Cycle Register 3 */
#define PWMPCR3 __REG(0x40C00018) /* PWM Period Control Register 3 */

/* -- defines for strobo */

#define GPIO_SSPSCLK	23
#define GPIO_SSPSFRM	24
#define GPIO_SSPTxD		25
#define	GPIO_SSPRxD		26
#define GPIO_SPI_CS		22
#define GPIO_INDEX		35
#define GPIO_TRIGGER	16

#define LABL_SSPSCLK	"SSPSCLK"
#define LABL_SSPSFRM	"SPSFRM"
#define LABL_SSPTxD		"SSPTxD"
#define LABL_SSPRxD		"SSPRxD"
#define LABL_SPI_CS		"SPI_CS"
#define LABL_INDEX		"INDEX"
#define LABL_TRIGGER	"TRIGGER"

#define SSCR0_P1  __REG(0x41000000)  /* SSP Port 1 Control Register 0 */
#define SSCR1_P1  __REG(0x41000004)  /* SSP Port 1 Control Register 1 */
#define SSSR_P1   __REG(0x41000008)  /* SSP Port 1 Status Register */
#define SSITR_P1  __REG(0x4100000C)  /* SSP Port 1 Interrupt Test Register */
#define SSDR_P1   __REG(0x41000010)  /* (Write / Read) SSP Port 1 Data Write Register/SSP Data Read Register */

#endif

/* -- structs ----------------------------------------------------------------*/

/* -- data structure for this device  */
typedef struct coffee2dev_t {
	struct cdev cdev_service;		
	struct cdev cdev_display;		
	struct cdev cdev_buttons;		
	struct cdev cdev_cakemtr;
	struct cdev cdev_cakermv;
	struct cdev cdev_pwdrmtr;
	struct cdev cdev_coffgrn;
	struct cdev cdev_coffdfd;
	struct cdev cdev_watrtmp;
	struct cdev cdev_watrhtr;
	struct cdev cdev_watrmtr;
	struct cdev cdev_watrpmp;
	struct cdev cdev_milkmtr;
	struct cdev cdev_milkpmp;
	struct cdev cdev_flshmtr;
	struct cdev cdev_flshpmp;
	struct cdev cdev_nvaumtr;
	atomic_t cdev_service_val;
	atomic_t cdev_display_val;
	atomic_t cdev_buttons_val;
	atomic_t cdev_cakemtr_val;
	atomic_t cdev_cakermv_val;
	atomic_t cdev_pwdrmtr_val;
	atomic_t cdev_coffgrn_val;
	atomic_t cdev_coffdfd_val;
	atomic_t cdev_watrtmp_val;
	atomic_t cdev_watrhtr_val;
	atomic_t cdev_watrmtr_val;
	atomic_t cdev_watrpmp_val;
	atomic_t cdev_milkmtr_val;
	atomic_t cdev_milkpmp_val;
	atomic_t cdev_flshmtr_val;
	atomic_t cdev_flshpmp_val;
	atomic_t cdev_nvaumtr_val;
	/* -- shadow register led */
	atomic_t led_shadow_reg;
#ifdef ORCHID
	struct tasklet_struct tasklet_S5;
	struct tasklet_struct tasklet_S6;
	struct tasklet_struct tasklet_S7;
	struct tasklet_struct tasklet_S8;
#elif defined(CARME)
	/* -- memory mapping */
	struct resource *mem_res;
	void *io_base;
	/* -- shadow register switch */	
	u8 switch_shadow_reg;
	/* -- kfifo for buttons with corresponding spinlock and semaphore */
	struct kfifo * button_kfifo;
	spinlock_t button_kfifo_spinlock;	
	wait_queue_head_t button_kfifo_wait;
	/* -- kernel timer */
	struct timer_list timer;	
#ifdef USE_TASKLET	
	/* -- strobo tasklet */
	struct tasklet_struct tasklet_index;
#elif defined(USE_WORKQUEUE) 
	struct work_struct work_index;
#endif
	
#endif
} coffee2dev_t;

/* -- function declarations --------------------------------------------------*/

int cdev_service_open(struct inode * inode, struct file * filp);
ssize_t cdev_service_read(struct file * filp, char __user *buff, size_t count, loff_t *offp);
ssize_t cdev_service_write(struct file * filp, const char __user *buff, size_t count, loff_t *offp);
int cdev_service_release(struct inode * inode, struct file * filp);

int cdev_display_open(struct inode * inode, struct file * filp);
ssize_t cdev_display_read(struct file * filp, char __user *buff, size_t count, loff_t *offp);
ssize_t cdev_display_write(struct file * filp, const char __user *buff, size_t count, loff_t *offp);
int cdev_display_release(struct inode * inode, struct file * filp);

int cdev_buttons_open(struct inode * inode, struct file * filp);
ssize_t cdev_buttons_read(struct file * filp, char __user *buff, size_t count, loff_t *offp);
ssize_t cdev_buttons_write(struct file * filp, const char __user *buff, size_t count, loff_t *offp);
int cdev_buttons_release(struct inode * inode, struct file * filp);

int cdev_cakemtr_open(struct inode * inode, struct file * filp);
ssize_t cdev_cakemtr_read(struct file * filp, char __user *buff, size_t count, loff_t *offp);
ssize_t cdev_cakemtr_write(struct file * filp, const char __user *buff, size_t count, loff_t *offp);
int cdev_cakemtr_release(struct inode * inode, struct file * filp);

int cdev_cakermv_open(struct inode * inode, struct file * filp);
ssize_t cdev_cakermv_read(struct file * filp, char __user *buff, size_t count, loff_t *offp);
ssize_t cdev_cakermv_write(struct file * filp, const char __user *buff, size_t count, loff_t *offp);
int cdev_cakermv_release(struct inode * inode, struct file * filp);

int cdev_pwdrmtr_open(struct inode * inode, struct file * filp);
ssize_t cdev_pwdrmtr_read(struct file * filp, char __user *buff, size_t count, loff_t *offp);
ssize_t cdev_pwdrmtr_write(struct file * filp, const char __user *buff, size_t count, loff_t *offp);
int cdev_pwdrmtr_release(struct inode * inode, struct file * filp);

int cdev_coffgrn_open(struct inode * inode, struct file * filp);
ssize_t cdev_coffgrn_read(struct file * filp, char __user *buff, size_t count, loff_t *offp);
ssize_t cdev_coffgrn_write(struct file * filp, const char __user *buff, size_t count, loff_t *offp);
int cdev_coffgrn_release(struct inode * inode, struct file * filp);

int cdev_coffdfd_open(struct inode * inode, struct file * filp);
ssize_t cdev_coffdfd_read(struct file * filp, char __user *buff, size_t count, loff_t *offp);
ssize_t cdev_coffdfd_write(struct file * filp, const char __user *buff, size_t count, loff_t *offp);
int cdev_coffdfd_release(struct inode * inode, struct file * filp);

int cdev_watrtmp_open(struct inode * inode, struct file * filp);
ssize_t cdev_watrtmp_read(struct file * filp, char __user *buff, size_t count, loff_t *offp);
ssize_t cdev_watrtmp_write(struct file * filp, const char __user *buff, size_t count, loff_t *offp);
int cdev_watrtmp_release(struct inode * inode, struct file * filp);

int cdev_watrhtr_open(struct inode * inode, struct file * filp);
ssize_t cdev_watrhtr_read(struct file * filp, char __user *buff, size_t count, loff_t *offp);
ssize_t cdev_watrhtr_write(struct file * filp, const char __user *buff, size_t count, loff_t *offp);
int cdev_watrhtr_release(struct inode * inode, struct file * filp);

int cdev_watrmtr_open(struct inode * inode, struct file * filp);
ssize_t cdev_watrmtr_read(struct file * filp, char __user *buff, size_t count, loff_t *offp);
ssize_t cdev_watrmtr_write(struct file * filp, const char __user *buff, size_t count, loff_t *offp);
int cdev_watrmtr_release(struct inode * inode, struct file * filp);

int cdev_watrpmp_open(struct inode * inode, struct file * filp);
ssize_t cdev_watrpmp_read(struct file * filp, char __user *buff, size_t count, loff_t *offp);
ssize_t cdev_watrpmp_write(struct file * filp, const char __user *buff, size_t count, loff_t *offp);
int cdev_watrpmp_release(struct inode * inode, struct file * filp);

int cdev_milkmtr_open(struct inode * inode, struct file * filp);
ssize_t cdev_milkmtr_read(struct file * filp, char __user *buff, size_t count, loff_t *offp);
ssize_t cdev_milkmtr_write(struct file * filp, const char __user *buff, size_t count, loff_t *offp);
int cdev_milkmtr_release(struct inode * inode, struct file * filp);

int cdev_milkpmp_open(struct inode * inode, struct file * filp);
ssize_t cdev_milkpmp_read(struct file * filp, char __user *buff, size_t count, loff_t *offp);
ssize_t cdev_milkpmp_write(struct file * filp, const char __user *buff, size_t count, loff_t *offp);
int cdev_milkpmp_release(struct inode * inode, struct file * filp);

int cdev_flshmtr_open(struct inode * inode, struct file * filp);
ssize_t cdev_flshmtr_read(struct file * filp, char __user *buff, size_t count, loff_t *offp);
ssize_t cdev_flshmtr_write(struct file * filp, const char __user *buff, size_t count, loff_t *offp);
int cdev_flshmtr_release(struct inode * inode, struct file * filp);

int cdev_flshpmp_open(struct inode * inode, struct file * filp);
ssize_t cdev_flshpmp_read(struct file * filp, char __user *buff, size_t count, loff_t *offp);
ssize_t cdev_flshpmp_write(struct file * filp, const char __user *buff, size_t count, loff_t *offp);
int cdev_flshpmp_release(struct inode * inode, struct file * filp);

int cdev_nvaumtr_open(struct inode * inode, struct file * filp);
ssize_t cdev_nvaumtr_read(struct file * filp, char __user *buff, size_t count, loff_t *offp);
ssize_t cdev_nvaumtr_write(struct file * filp, const char __user *buff, size_t count, loff_t *offp);
int cdev_nvaumtr_release(struct inode * inode, struct file * filp);

irqreturn_t index_irq_handler(int irq, void *dev_id);
irqreturn_t button_irq_handler(int irq, void *dev_id); 
int init_gpio_button(int gpio, char *label);

#ifdef ORCHID

void tasklet_handler_S5(unsigned long data);
void tasklet_handler_S6(unsigned long data);
void tasklet_handler_S7(unsigned long data);
void tasklet_handler_S8(unsigned long data);
void tasklet_handler(unsigned long data);
int init_gpio_led(int gpio, char *label);

#elif defined(CARME)

void tasklet_handler_index(unsigned long data);
void work_handler_index(struct work_struct *work);
void timer_function(unsigned long arg);
void timer_arm(struct timer_list *timer, int interval);
void button_enqueue(struct kfifo *button_kfifo, unsigned char button);
int button_dequeue_wait(struct kfifo *button_kfifo, unsigned char *button);
int button_dequeue_nowait(struct kfifo *button_kfifo, unsigned char *button);
int init_gpio_pwm(int gpio_speed, char *label_speed, int gpio_direction, char *label_direction);
int init_gpio(int gpio, char *label, int flags);

#endif

int __init coffee2dev_init(void);
void __exit coffee2dev_exit(void);

/* -- static data ------------------------------------------------------------*/

/* -- pointer to the data structure for this device */ 
static struct coffee2dev_t * this;	

/* -- structure with callback functions for cdev_service */
static struct file_operations fops_cdev_service = {
	.owner		= THIS_MODULE,
	.open		= cdev_service_open,
	.release	= cdev_service_release,
	.read		= cdev_service_read,
	.write		= cdev_service_write,
	.llseek		= no_llseek,
};

/* -- structure with callback functions for cdev_display */
static struct file_operations fops_cdev_display = {
	.owner		= THIS_MODULE,
	.open		= cdev_display_open,
	.release	= cdev_display_release,
	.read		= cdev_display_read,
	.write		= cdev_display_write,
	.llseek		= no_llseek,
};

/* -- structure with callback functions for cdev_buttons */
static struct file_operations fops_cdev_buttons = {
	.owner		= THIS_MODULE,
	.open		= cdev_buttons_open,
	.release	= cdev_buttons_release,
	.read		= cdev_buttons_read,
	.write		= cdev_buttons_write,
	.llseek		= no_llseek,
};

/* -- structure with callback functions for cdev_cakemtr */
static struct file_operations fops_cdev_cakemtr = {
	.owner		= THIS_MODULE,
	.open		= cdev_cakemtr_open,
	.release	= cdev_cakemtr_release,
	.read		= cdev_cakemtr_read,
	.write		= cdev_cakemtr_write,
	.llseek		= no_llseek,
};

/* -- structure with callback functions for cdev_cakermv */
static struct file_operations fops_cdev_cakermv = {
	.owner		= THIS_MODULE,
	.open		= cdev_cakermv_open,
	.release	= cdev_cakermv_release,
	.read		= cdev_cakermv_read,
	.write		= cdev_cakermv_write,
	.llseek		= no_llseek,
};

/* -- structure with callback functions for cdev_pwdrmtr */
static struct file_operations fops_cdev_pwdrmtr = {
	.owner		= THIS_MODULE,
	.open		= cdev_pwdrmtr_open,
	.release	= cdev_pwdrmtr_release,
	.read		= cdev_pwdrmtr_read,
	.write		= cdev_pwdrmtr_write,
	.llseek		= no_llseek,
};

/* -- structure with callback functions for cdev_coffgrn */
static struct file_operations fops_cdev_coffgrn = {
	.owner		= THIS_MODULE,
	.open		= cdev_coffgrn_open,
	.release	= cdev_coffgrn_release,
	.read		= cdev_coffgrn_read,
	.write		= cdev_coffgrn_write,
	.llseek		= no_llseek,
};

/* -- structure with callback functions for cdev_coffdfd */
static struct file_operations fops_cdev_coffdfd = {
	.owner		= THIS_MODULE,
	.open		= cdev_coffdfd_open,
	.release	= cdev_coffdfd_release,
	.read		= cdev_coffdfd_read,
	.write		= cdev_coffdfd_write,
	.llseek		= no_llseek,
};

/* -- structure with callback functions for cdev_watrtmp */
static struct file_operations fops_cdev_watrtmp = {
	.owner		= THIS_MODULE,
	.open		= cdev_watrtmp_open,
	.release	= cdev_watrtmp_release,
	.read		= cdev_watrtmp_read,
	.write		= cdev_watrtmp_write,
	.llseek		= no_llseek,
};

/* -- structure with callback functions for cdev_watrhtr */
static struct file_operations fops_cdev_watrhtr = {
	.owner		= THIS_MODULE,
	.open		= cdev_watrhtr_open,
	.release	= cdev_watrhtr_release,
	.read		= cdev_watrhtr_read,
	.write		= cdev_watrhtr_write,
	.llseek		= no_llseek,
};

/* -- structure with callback functions for cdev_watrmtr */
static struct file_operations fops_cdev_watrmtr = {
	.owner		= THIS_MODULE,
	.open		= cdev_watrmtr_open,
	.release	= cdev_watrmtr_release,
	.read		= cdev_watrmtr_read,
	.write		= cdev_watrmtr_write,
	.llseek		= no_llseek,
};

/* -- structure with callback functions for cdev_watrpmp */
static struct file_operations fops_cdev_watrpmp = {
	.owner		= THIS_MODULE,
	.open		= cdev_watrpmp_open,
	.release	= cdev_watrpmp_release,
	.read		= cdev_watrpmp_read,
	.write		= cdev_watrpmp_write,
	.llseek		= no_llseek,
};

/* -- structure with callback functions for cdev_milkmtr */
static struct file_operations fops_cdev_milkmtr = {
	.owner		= THIS_MODULE,
	.open		= cdev_milkmtr_open,
	.release	= cdev_milkmtr_release,
	.read		= cdev_milkmtr_read,
	.write		= cdev_milkmtr_write,
	.llseek		= no_llseek,
};

/* -- structure with callback functions for cdev_milkpmp */
static struct file_operations fops_cdev_milkpmp = {
	.owner		= THIS_MODULE,
	.open		= cdev_milkpmp_open,
	.release	= cdev_milkpmp_release,
	.read		= cdev_milkpmp_read,
	.write		= cdev_milkpmp_write,
	.llseek		= no_llseek,
};

/* -- structure with callback functions for cdev_flshmtr */
static struct file_operations fops_cdev_flshmtr = {
	.owner		= THIS_MODULE,
	.open		= cdev_flshmtr_open,
	.release	= cdev_flshmtr_release,
	.read		= cdev_flshmtr_read,
	.write		= cdev_flshmtr_write,
	.llseek		= no_llseek,
};

/* -- structure with callback functions for cdev_flshpmp */
static struct file_operations fops_cdev_flshpmp = {
	.owner		= THIS_MODULE,
	.open		= cdev_flshpmp_open,
	.release	= cdev_flshpmp_release,
	.read		= cdev_flshpmp_read,
	.write		= cdev_flshpmp_write,
	.llseek		= no_llseek,
};

/* -- structure with callback functions for cdev_nvaumtr */
static struct file_operations fops_cdev_nvaumtr = {
	.owner		= THIS_MODULE,
	.open		= cdev_nvaumtr_open,
	.release	= cdev_nvaumtr_release,
	.read		= cdev_nvaumtr_read,
	.write		= cdev_nvaumtr_write,
	.llseek		= no_llseek,
};

/* -- function definitions ---------------------------------------------------*/

/* -- file operations for cdev_service */
int cdev_service_open(struct inode * inode_p, struct file * file_p) 
{
	int ret = 0;
	
	printk(KERN_DEBUG "coffee2dev->cdev_service_open: opened by \"%s\" (pid %i) on \"%s\"\n",
		current->comm,
		current->pid,
		file_p->f_path.dentry->d_name.name);
	
	ret = nonseekable_open(inode_p, file_p);
	return ret;
}

ssize_t cdev_service_read(struct file * filp, char __user *buff, size_t count, loff_t *offp)
{
	ssize_t ret = 0;

	printk(KERN_DEBUG "coffee2dev->cdev_service_read: read by \"%s\" (pid %i)\n",
		current->comm,
		current->pid);
	
	if (*offp == 0 && count >= sizeof(int)) {
		// -- service interface is like a channel: read resets the value
		int val = atomic_xchg(&this->cdev_service_val, -1);
		if (copy_to_user(buff, &val, sizeof(val))) {
			ret = -EFAULT;
		}
		else {
			ret = sizeof(val);
			*offp += ret;
			printk(KERN_DEBUG "coffee2dev->cdev_service_read: read %d\n", val);
		}
	}
	return ret;
}

ssize_t cdev_service_write(struct file * filp, const char __user *buff, size_t count, loff_t *offp)
{
	int val = 0;
	ssize_t ret = count;

	printk(KERN_DEBUG "coffee2dev->cdev_service_write: write by \"%s\" (pid %i), count=%lud\n",
		current->comm,
		current->pid,
		(unsigned long)count);

	if (count == sizeof(val)) {
		if (copy_from_user(&val, buff, count)) {
			ret = -EFAULT;
		}
		else {
			atomic_set(&this->cdev_service_val, val);
			*offp += count;
			printk(KERN_DEBUG "coffee2dev->cdev_service_write: written %d\n", val);
		}
	}
	else {
		ret = -EINVAL;
	}
	return ret;
}

int cdev_service_release(struct inode * inode_p, struct file * file_p) 
{
	printk(KERN_DEBUG "coffee2dev->cdev_service_release: released by \"%s\" (pid %i) on \"%s\"\n",
		current->comm,
		current->pid,
		file_p->f_path.dentry->d_name.name);
	return 0;
}

/* -- file operations for cdev_display */
int cdev_display_open(struct inode * inode_p, struct file * file_p) 
{
	int ret = 0;
	
	printk(KERN_DEBUG "coffee2dev->cdev_display_open: opened by \"%s\" (pid %i) on \"%s\"\n",
		current->comm,
		current->pid,
		file_p->f_path.dentry->d_name.name);
		
	ret = nonseekable_open(inode_p, file_p);
	return ret;
}

ssize_t cdev_display_read(struct file * filp, char __user *buff, size_t count, loff_t *offp)
{
	u8 u8val = 0;
	ssize_t ret = 0;

	printk(KERN_DEBUG "coffee2dev->cdev_display_read: read by \"%s\" (pid %i)\n",
		current->comm,
		current->pid);
	
	if (count >= sizeof(u8val)) {
		
		// -- button is more like a channel: read resets the value		
		u8val = (u8)atomic_read(&this->led_shadow_reg);
		if (copy_to_user(buff, &u8val, sizeof(u8val))) {
			ret = -EFAULT;
		}
		else {
			ret = sizeof(u8val);			
		}
	}
	return ret;
}

ssize_t cdev_display_write(struct file * filp, const char __user *buff, size_t count, loff_t *offp)
{
	u8 u8val;
	ssize_t ret = count;

	printk(KERN_DEBUG "coffee2dev->cdev_display_write: write by \"%s\" (pid %i), count=%lud\n",
		current->comm,
		current->pid,
		(unsigned long)count);
		
	if (count >= sizeof(u8val)) {
				
		if (get_user(u8val, buff) != 0) {
			ret =  -EFAULT;
		}
		else {
			
			atomic_set(&this->led_shadow_reg, u8val); 

#ifdef ORCHID	

			int bit0 = (u8val & 1);
			int bit1 = (u8val & 2) >> 1;
			int bit2 = (u8val & 4) >> 2;
			int bit3 = (u8val & 8) >> 3;
			int bit4 = (u8val & 16) >> 4;
			int bit5 = (u8val & 32) >> 5;
			int bit6 = (u8val & 64) >> 6;
			int bit7 = (u8val & 128) >> 7;
			gpio_set_value(GPIO_V1, bit0);
			gpio_set_value(GPIO_V2, bit1);
			gpio_set_value(GPIO_V3, bit2);
			gpio_set_value(GPIO_V4, bit3);
			gpio_set_value(GPIO_V5, bit4);
			gpio_set_value(GPIO_V6, bit5);
			gpio_set_value(GPIO_V7, bit6);
			gpio_set_value(GPIO_V8, bit7);
		
#elif defined(CARME)			
			
			iowrite8(u8val, this->io_base + LED_OFFSET);	
		
#endif
		}
	}
	return ret;
}

int cdev_display_release(struct inode * inode_p, struct file * file_p) 
{
	printk(KERN_DEBUG "coffee2dev->cdev_display_release: released by \"%s\" (pid %i) on \"%s\"\n",
		current->comm,
		current->pid,
		file_p->f_path.dentry->d_name.name);
	return 0;
}

/* -- file operations for cdev_buttons */
int cdev_buttons_open(struct inode * inode_p, struct file * file_p) 
{
	int ret = 0;
	
	printk(KERN_DEBUG "coffee2dev->cdev_buttons_open: opened by \"%s\" (pid %i) on \"%s\"\n",
		current->comm,
		current->pid,
		file_p->f_path.dentry->d_name.name);
		
	ret = nonseekable_open(inode_p, file_p);		
	return ret;
}

ssize_t cdev_buttons_read(struct file * filp, char __user *buff, size_t count, loff_t *offp)
{
	ssize_t ret = 0;

	printk(KERN_DEBUG "coffee2dev->cdev_buttons_read: read by \"%s\" (pid %i)\n",
		current->comm,
		current->pid);
		
#ifdef ORCHID
	if (*offp == 0 && count >= sizeof(int)) {
		// -- button is more like a channel: read resets the value
        int val = atomic_xchg(&this->cdev_buttons_val, -1);
		if (copy_to_user(buff, &val, sizeof(val))) {
			ret = -EFAULT;
		}
		else {
			ret = sizeof(val);
			*offp += ret;
		}
	}
#elif defined(CARME)	
	if (count >= sizeof(int)) {		
		int val = 0;		
		unsigned char button;
		if (filp->f_flags & O_NONBLOCK) {
			/* -- nonblocking read */			
			ret = button_dequeue_nowait(this->button_kfifo, &button);
			if (ret == 0) {
				ret = -EWOULDBLOCK;
			}
			else {
				val = button;
			}
		}
		else {
			/* -- blocking read */
			ret = button_dequeue_wait(this->button_kfifo, &button);
			val = button;
		}
		if (ret > 0) {		
			/* -- copy value to user */
			ret = sizeof(val);
			if (copy_to_user(buff, &val, sizeof(val))) {
				ret = -EFAULT;
			}
		}
	}
#endif	
	return ret;
}

ssize_t cdev_buttons_write(struct file * filp, const char __user *buff, size_t count, loff_t *offp)
{	
	ssize_t ret = count;

	printk(KERN_DEBUG "coffee2dev->cdev_buttons_write: write by \"%s\" (pid %i), count=%lud\n",
		current->comm,
		current->pid,
		(unsigned long)count);
		
	kfifo_reset(this->button_kfifo);
	
	return ret;
}

int cdev_buttons_release(struct inode * inode_p, struct file * file_p) 
{
	printk(KERN_DEBUG "coffee2dev->cdev_buttons_release: released by \"%s\" (pid %i) on \"%s\"\n",
		current->comm,
		current->pid,
		file_p->f_path.dentry->d_name.name);
	return 0;
}

/* -- file operations for cdev_cakemtr */
int cdev_cakemtr_open(struct inode * inode_p, struct file * file_p) 
{
	int ret = 0;
	
	printk(KERN_DEBUG "coffee2dev->cdev_cakemtr_open: opened by \"%s\" (pid %i) on \"%s\"\n",
		current->comm,
		current->pid,
		file_p->f_path.dentry->d_name.name);
	
	ret = nonseekable_open(inode_p, file_p);
	return ret;
}

ssize_t cdev_cakemtr_read(struct file * filp, char __user *buff, size_t count, loff_t *offp)
{
	ssize_t ret = 0;

	printk(KERN_DEBUG "coffee2dev->cdev_cakemtr_read: read by \"%s\" (pid %i)\n",
		current->comm,
		current->pid);
	
	if (*offp == 0 && count >= sizeof(int)) {
		// -- button is more like a channel: read resets the value
		int val = atomic_xchg(&this->cdev_cakemtr_val, -1);
		if (copy_to_user(buff, &val, sizeof(val))) {
			ret = -EFAULT;
		}
		else {
			ret = sizeof(val);
			//*offp += ret;
		}
	}
	return ret;
}

ssize_t cdev_cakemtr_write(struct file * filp, const char __user *buff, size_t count, loff_t *offp)
{
	int val = 0;
	ssize_t ret = count;

	printk(KERN_DEBUG "coffee2dev->cdev_cakemtr_write: write by \"%s\" (pid %i), count=%lud\n",
		current->comm,
		current->pid,
		(unsigned long)count);

	if (count == sizeof(val)) {
		if (copy_from_user(&val, buff, count)) {
			ret = -EFAULT;
		}
		else {
			atomic_set(&this->cdev_cakemtr_val, val);
			*offp += count;
		}
	}
	else {
		ret = -EINVAL;
	}
	return ret;
}

int cdev_cakemtr_release(struct inode * inode_p, struct file * file_p) 
{
	printk(KERN_DEBUG "coffee2dev->cdev_cakemtr_release: released by \"%s\" (pid %i) on \"%s\"\n",
		current->comm,
		current->pid,
		file_p->f_path.dentry->d_name.name);
	return 0;
}

/* -- file operations for cdev_cakermv */
int cdev_cakermv_open(struct inode * inode_p, struct file * file_p) 
{
	int ret = 0;
	
	printk(KERN_DEBUG "coffee2dev->cdev_cakermv_open: opened by \"%s\" (pid %i) on \"%s\"\n",
		current->comm,
		current->pid,
		file_p->f_path.dentry->d_name.name);
	
	ret = nonseekable_open(inode_p, file_p);
	return ret;
}

ssize_t cdev_cakermv_read(struct file * filp, char __user *buff, size_t count, loff_t *offp)
{
	ssize_t ret = 0;

	printk(KERN_DEBUG "coffee2dev->cdev_cakermv_read: read by \"%s\" (pid %i)\n",
		current->comm,
		current->pid);
	
	if (*offp == 0 && count >= sizeof(int)) {
		// -- button is more like a channel: read resets the value
		int val = atomic_xchg(&this->cdev_cakermv_val, -1);
		if (copy_to_user(buff, &val, sizeof(val))) {
			ret = -EFAULT;
		}
		else {
			ret = sizeof(val);
			//*offp += ret;
		}
	}
	return ret;
}

ssize_t cdev_cakermv_write(struct file * filp, const char __user *buff, size_t count, loff_t *offp)
{
	int val = 0;
	ssize_t ret = count;

	printk(KERN_DEBUG "coffee2dev->cdev_cakermv_write: write by \"%s\" (pid %i), count=%lud\n",
		current->comm,
		current->pid,
		(unsigned long)count);

	if (count == sizeof(val)) {
		if (copy_from_user(&val, buff, count)) {
			ret = -EFAULT;
		}
		else {
			atomic_set(&this->cdev_cakermv_val, val);
			*offp += count;
		}
	}
	else {
		ret = -EINVAL;
	}
	return ret;
}

int cdev_cakermv_release(struct inode * inode_p, struct file * file_p) 
{
	printk(KERN_DEBUG "coffee2dev->cdev_cakermv_release: released by \"%s\" (pid %i) on \"%s\"\n",
		current->comm,
		current->pid,
		file_p->f_path.dentry->d_name.name);
	return 0;
}


/* -- file operations for cdev_pwdrmtr */
int cdev_pwdrmtr_open(struct inode * inode_p, struct file * file_p) 
{
	int ret = 0;
	
	printk(KERN_DEBUG "coffee2dev->cdev_pwdrmtr_open: opened by \"%s\" (pid %i) on \"%s\"\n",
		current->comm,
		current->pid,
		file_p->f_path.dentry->d_name.name);
	
	ret = nonseekable_open(inode_p, file_p);
	return ret;
}

ssize_t cdev_pwdrmtr_read(struct file * filp, char __user *buff, size_t count, loff_t *offp)
{
	ssize_t ret = 0;

	printk(KERN_DEBUG "coffee2dev->cdev_pwdrmtr_read: read by \"%s\" (pid %i)\n",
		current->comm,
		current->pid);
	
	if (*offp == 0 && count >= sizeof(int)) {
		int val = atomic_read(&this->cdev_pwdrmtr_val);
		if (copy_to_user(buff, &val, sizeof(val))) {
			ret = -EFAULT;
		}
		else {
			ret = sizeof(val);
			*offp += ret;
		}
	}
	return ret;
}

ssize_t cdev_pwdrmtr_write(struct file * filp, const char __user *buff, size_t count, loff_t *offp)
{
	int val = 0;
	ssize_t ret = count;

	printk(KERN_DEBUG "coffee2dev->cdev_pwdrmtr_write: write by \"%s\" (pid %i), count=%lud\n",
		current->comm,
		current->pid,
		(unsigned long)count);

	if (count == sizeof(val)) {
		if (copy_from_user(&val, buff, count)) {
			ret = -EFAULT;
		}
		else {
			atomic_set(&this->cdev_pwdrmtr_val, val);
			*offp += count;
		}
	}
	else {
		ret = -EINVAL;
	}
	return ret;
}

int cdev_pwdrmtr_release(struct inode * inode_p, struct file * file_p) 
{
	printk(KERN_DEBUG "coffee2dev->cdev_pwdrmtr_release: released by \"%s\" (pid %i) on \"%s\"\n",
		current->comm,
		current->pid,
		file_p->f_path.dentry->d_name.name);
	return 0;
}

/* -- file operations for cdev_coffgrn */
int cdev_coffgrn_open(struct inode * inode_p, struct file * file_p) 
{
	int ret = 0;
	
	printk(KERN_DEBUG "coffee2dev->cdev_coffgrn_open: opened by \"%s\" (pid %i) on \"%s\"\n",
		current->comm,
		current->pid,
		file_p->f_path.dentry->d_name.name);
	
	ret = nonseekable_open(inode_p, file_p);
	return ret;
}

ssize_t cdev_coffgrn_read(struct file * filp, char __user *buff, size_t count, loff_t *offp)
{
	ssize_t ret = 0;

	printk(KERN_DEBUG "coffee2dev->cdev_coffgrn_read: read by \"%s\" (pid %i)\n",
		current->comm,
		current->pid);
	
	if (*offp == 0 && count >= sizeof(int)) {
		int val = atomic_read(&this->cdev_coffgrn_val);
		if (copy_to_user(buff, &val, sizeof(val))) {
			ret = -EFAULT;
		}
		else {
			ret = sizeof(val);
			*offp += ret;
		}
	}
	return ret;
}

ssize_t cdev_coffgrn_write(struct file * filp, const char __user *buff, size_t count, loff_t *offp)
{
	int val = 0;
	ssize_t ret = count;

	printk(KERN_DEBUG "coffee2dev->cdev_coffgrn_write: write by \"%s\" (pid %i), count=%lud\n",
		current->comm,
		current->pid,
		(unsigned long)count);

	if (count == sizeof(val)) {
		if (copy_from_user(&val, buff, count)) {
			ret = -EFAULT;
		}
		else {
			if (val > 100) {
				val = 100;
			}
			if (val < 0) {
				val = 0;
			}
			printk(KERN_DEBUG "coffee2dev->cdev_coffgrn_write: %d", val);
			atomic_set(&this->cdev_coffgrn_val, val);
			PWMDCR3 = val;
		}
	}
	else {
		ret = -EINVAL;
	}
	return ret;
}

int cdev_coffgrn_release(struct inode * inode_p, struct file * file_p) 
{
	printk(KERN_DEBUG "coffee2dev->cdev_coffgrn_release: released by \"%s\" (pid %i) on \"%s\"\n",
		current->comm,
		current->pid,
		file_p->f_path.dentry->d_name.name);
	return 0;
}

/* -- file operations for cdev_coffdfd */
int cdev_coffdfd_open(struct inode * inode_p, struct file * file_p) 
{
	int ret = 0;
	
	printk(KERN_DEBUG "coffee2dev->cdev_coffdfd_open: opened by \"%s\" (pid %i) on \"%s\"\n",
		current->comm,
		current->pid,
		file_p->f_path.dentry->d_name.name);
	
	ret = nonseekable_open(inode_p, file_p);
	return ret;
}

ssize_t cdev_coffdfd_read(struct file * filp, char __user *buff, size_t count, loff_t *offp)
{
	ssize_t ret = 0;

	printk(KERN_DEBUG "coffee2dev->cdev_coffdfd_read: read by \"%s\" (pid %i)\n",
		current->comm,
		current->pid);
	
	if (*offp == 0 && count >= sizeof(int)) {
		int val = atomic_read(&this->cdev_coffdfd_val);
		if (copy_to_user(buff, &val, sizeof(val))) {
			ret = -EFAULT;
		}
		else {
			ret = sizeof(val);
			*offp += ret;
		}
	}
	return ret;
}

ssize_t cdev_coffdfd_write(struct file * filp, const char __user *buff, size_t count, loff_t *offp)
{
	int val = 0;
	ssize_t ret = count;

	printk(KERN_DEBUG "coffee2dev->cdev_coffdfd_write: write by \"%s\" (pid %i), count=%lud\n",
		current->comm,
		current->pid,
		(unsigned long)count);

	if (count == sizeof(val)) {
		if (copy_from_user(&val, buff, count)) {
			ret = -EFAULT;
		}
		else {
			atomic_set(&this->cdev_coffdfd_val, val);
			*offp += count;
		}
	}
	else {
		ret = -EINVAL;
	}
	return ret;
}

int cdev_coffdfd_release(struct inode * inode_p, struct file * file_p) 
{
	printk(KERN_DEBUG "coffee2dev->cdev_coffdfd_release: released by \"%s\" (pid %i) on \"%s\"\n",
		current->comm,
		current->pid,
		file_p->f_path.dentry->d_name.name);
	return 0;
}

/* -- file operations for cdev_watrtmp */
int cdev_watrtmp_open(struct inode * inode_p, struct file * file_p) 
{
	int ret = 0;
	
	printk(KERN_DEBUG "coffee2dev->cdev_watrtmp_open: opened by \"%s\" (pid %i) on \"%s\"\n",
		current->comm,
		current->pid,
		file_p->f_path.dentry->d_name.name);
	
	ret = nonseekable_open(inode_p, file_p);
	return ret;
}

ssize_t cdev_watrtmp_read(struct file * filp, char __user *buff, size_t count, loff_t *offp)
{
	ssize_t ret = 0;

	printk(KERN_DEBUG "coffee2dev->cdev_watrtmp_read: read by \"%s\" (pid %i)\n",
		current->comm,
		current->pid);
	
	if (*offp == 0 && count >= sizeof(int)) {
		int val = atomic_read(&this->cdev_watrtmp_val);
		if (copy_to_user(buff, &val, sizeof(val))) {
			ret = -EFAULT;
		}
		else {
			ret = sizeof(val);
			*offp += ret;
		}
	}
	return ret;
}

ssize_t cdev_watrtmp_write(struct file * filp, const char __user *buff, size_t count, loff_t *offp)
{
	int val = 0;
	ssize_t ret = count;

	printk(KERN_DEBUG "coffee2dev->cdev_watrtmp_write: write by \"%s\" (pid %i), count=%lud\n",
		current->comm,
		current->pid,
		(unsigned long)count);

	if (count == sizeof(val)) {
		if (copy_from_user(&val, buff, count)) {
			ret = -EFAULT;
		}
		else {
			atomic_set(&this->cdev_watrtmp_val, val);
			*offp += count;
		}
	}
	else {
		ret = -EINVAL;
	}
	return ret;
}

int cdev_watrtmp_release(struct inode * inode_p, struct file * file_p) 
{
	printk(KERN_DEBUG "coffee2dev->cdev_watrtmp_release: released by \"%s\" (pid %i) on \"%s\"\n",
		current->comm,
		current->pid,
		file_p->f_path.dentry->d_name.name);
	return 0;
}

/* -- file operations for cdev_watrhtr */
int cdev_watrhtr_open(struct inode * inode_p, struct file * file_p) 
{
	int ret = 0;
	
	printk(KERN_DEBUG "coffee2dev->cdev_watrhtr_open: opened by \"%s\" (pid %i) on \"%s\"\n",
		current->comm,
		current->pid,
		file_p->f_path.dentry->d_name.name);
	
	ret = nonseekable_open(inode_p, file_p);
	return ret;
}

ssize_t cdev_watrhtr_read(struct file * filp, char __user *buff, size_t count, loff_t *offp)
{
	ssize_t ret = 0;

	printk(KERN_DEBUG "coffee2dev->cdev_watrhtr_read: read by \"%s\" (pid %i)\n",
		current->comm,
		current->pid);
	
	if (*offp == 0 && count >= sizeof(int)) {
		int val = atomic_read(&this->cdev_watrhtr_val);
		if (copy_to_user(buff, &val, sizeof(val))) {
			ret = -EFAULT;
		}
		else {
			ret = sizeof(val);
			*offp += ret;
		}
	}
	return ret;
}

ssize_t cdev_watrhtr_write(struct file * filp, const char __user *buff, size_t count, loff_t *offp)
{
	int val = 0;
	ssize_t ret = count;

	printk(KERN_DEBUG "coffee2dev->cdev_watrhtr_write: write by \"%s\" (pid %i), count=%lud\n",
		current->comm,
		current->pid,
		(unsigned long)count);

	if (count == sizeof(val)) {
		if (copy_from_user(&val, buff, count)) {
			ret = -EFAULT;
		}
		else {
			atomic_set(&this->cdev_watrhtr_val, val);
			*offp += count;
		}
	}
	else {
		ret = -EINVAL;
	}
	return ret;
}

int cdev_watrhtr_release(struct inode * inode_p, struct file * file_p) 
{
	printk(KERN_DEBUG "coffee2dev->cdev_watrhtr_release: released by \"%s\" (pid %i) on \"%s\"\n",
		current->comm,
		current->pid,
		file_p->f_path.dentry->d_name.name);
	return 0;
}

/* -- file operations for cdev_watrmtr */
int cdev_watrmtr_open(struct inode * inode_p, struct file * file_p) 
{
	int ret = 0;
	
	printk(KERN_DEBUG "coffee2dev->cdev_watrmtr_open: opened by \"%s\" (pid %i) on \"%s\"\n",
		current->comm,
		current->pid,
		file_p->f_path.dentry->d_name.name);
	
	ret = nonseekable_open(inode_p, file_p);
	return ret;
}

ssize_t cdev_watrmtr_read(struct file * filp, char __user *buff, size_t count, loff_t *offp)
{
	ssize_t ret = 0;

	printk(KERN_DEBUG "coffee2dev->cdev_watrmtr_read: read by \"%s\" (pid %i)\n",
		current->comm,
		current->pid);
	
	if (*offp == 0 && count >= sizeof(int)) {
		int val = atomic_read(&this->cdev_watrmtr_val);
		if (copy_to_user(buff, &val, sizeof(val))) {
			ret = -EFAULT;
		}
		else {
			ret = sizeof(val);
			*offp += ret;
		}
	}
	return ret;
}

ssize_t cdev_watrmtr_write(struct file * filp, const char __user *buff, size_t count, loff_t *offp)
{
	int val = 0;
	ssize_t ret = count;

	printk(KERN_DEBUG "coffee2dev->cdev_watrmtr_write: write by \"%s\" (pid %i), count=%lud\n",
		current->comm,
		current->pid,
		(unsigned long)count);

	if (count == sizeof(val)) {
		if (copy_from_user(&val, buff, count)) {
			ret = -EFAULT;
		}
		else {
			atomic_set(&this->cdev_watrmtr_val, val);
			*offp += count;
		}
	}
	else {
		ret = -EINVAL;
	}
	return ret;
}

int cdev_watrmtr_release(struct inode * inode_p, struct file * file_p) 
{
	printk(KERN_DEBUG "coffee2dev->cdev_watrmtr_release: released by \"%s\" (pid %i) on \"%s\"\n",
		current->comm,
		current->pid,
		file_p->f_path.dentry->d_name.name);
	return 0;
}

/* -- file operations for cdev_watrpmp */
int cdev_watrpmp_open(struct inode * inode_p, struct file * file_p) 
{
	int ret = 0;
	
	printk(KERN_DEBUG "coffee2dev->cdev_watrpmp_open: opened by \"%s\" (pid %i) on \"%s\"\n",
		current->comm,
		current->pid,
		file_p->f_path.dentry->d_name.name);
	
	ret = nonseekable_open(inode_p, file_p);
	return ret;
}

ssize_t cdev_watrpmp_read(struct file * filp, char __user *buff, size_t count, loff_t *offp)
{
	ssize_t ret = 0;

	printk(KERN_DEBUG "coffee2dev->cdev_watrpmp_read: read by \"%s\" (pid %i)\n",
		current->comm,
		current->pid);
	
	if (*offp == 0 && count >= sizeof(int)) {
		int val = atomic_read(&this->cdev_watrpmp_val);
		if (copy_to_user(buff, &val, sizeof(val))) {
			ret = -EFAULT;
		}
		else {
			ret = sizeof(val);
			*offp += ret;
		}
	}
	return ret;
}

ssize_t cdev_watrpmp_write(struct file * filp, const char __user *buff, size_t count, loff_t *offp)
{
	int val = 0;
	ssize_t ret = count;

	printk(KERN_DEBUG "coffee2dev->cdev_watrpmp_write: write by \"%s\" (pid %i), count=%lud\n",
		current->comm,
		current->pid,
		(unsigned long)count);

	if (count == sizeof(val)) {
		if (copy_from_user(&val, buff, count)) {
			ret = -EFAULT;
		}
		else {
			atomic_set(&this->cdev_watrpmp_val, val);
			*offp += count;
		}
	}
	else {
		ret = -EINVAL;
	}
	return ret;
}

int cdev_watrpmp_release(struct inode * inode_p, struct file * file_p) 
{
	printk(KERN_DEBUG "coffee2dev->cdev_watrpmp_release: released by \"%s\" (pid %i) on \"%s\"\n",
		current->comm,
		current->pid,
		file_p->f_path.dentry->d_name.name);
	return 0;
}

/* -- file operations for cdev_milkmtr */
int cdev_milkmtr_open(struct inode * inode_p, struct file * file_p) 
{
	int ret = 0;
	
	printk(KERN_DEBUG "coffee2dev->cdev_milkmtr_open: opened by \"%s\" (pid %i) on \"%s\"\n",
		current->comm,
		current->pid,
		file_p->f_path.dentry->d_name.name);
	
	ret = nonseekable_open(inode_p, file_p);
	return ret;
}

ssize_t cdev_milkmtr_read(struct file * filp, char __user *buff, size_t count, loff_t *offp)
{
	ssize_t ret = 0;

	printk(KERN_DEBUG "coffee2dev->cdev_milkmtr_read: read by \"%s\" (pid %i)\n",
		current->comm,
		current->pid);
	
	if (*offp == 0 && count >= sizeof(int)) {
		int val = atomic_read(&this->cdev_milkmtr_val);
		if (copy_to_user(buff, &val, sizeof(val))) {
			ret = -EFAULT;
		}
		else {
			ret = sizeof(val);
			*offp += ret;
		}
	}
	return ret;
}

ssize_t cdev_milkmtr_write(struct file * filp, const char __user *buff, size_t count, loff_t *offp)
{
	int val = 0;
	ssize_t ret = count;

	printk(KERN_DEBUG "coffee2dev->cdev_milkmtr_write: write by \"%s\" (pid %i), count=%lud\n",
		current->comm,
		current->pid,
		(unsigned long)count);

	if (count == sizeof(val)) {
		if (copy_from_user(&val, buff, count)) {
			ret = -EFAULT;
		}
		else {
			atomic_set(&this->cdev_milkmtr_val, val);
			*offp += count;
		}
	}
	else {
		ret = -EINVAL;
	}
	return ret;
}

int cdev_milkmtr_release(struct inode * inode_p, struct file * file_p) 
{
	printk(KERN_DEBUG "coffee2dev->cdev_milkmtr_release: released by \"%s\" (pid %i) on \"%s\"\n",
		current->comm,
		current->pid,
		file_p->f_path.dentry->d_name.name);
	return 0;
}

/* -- file operations for cdev_milkpmp */
int cdev_milkpmp_open(struct inode * inode_p, struct file * file_p) 
{
	int ret = 0;
	
	printk(KERN_DEBUG "coffee2dev->cdev_milkpmp_open: opened by \"%s\" (pid %i) on \"%s\"\n",
		current->comm,
		current->pid,
		file_p->f_path.dentry->d_name.name);
	
	ret = nonseekable_open(inode_p, file_p);
	return ret;
}

ssize_t cdev_milkpmp_read(struct file * filp, char __user *buff, size_t count, loff_t *offp)
{
	ssize_t ret = 0;

	printk(KERN_DEBUG "coffee2dev->cdev_milkpmp_read: read by \"%s\" (pid %i)\n",
		current->comm,
		current->pid);
	
	if (*offp == 0 && count >= sizeof(int)) {
		int val = atomic_read(&this->cdev_milkpmp_val);
		if (copy_to_user(buff, &val, sizeof(val))) {
			ret = -EFAULT;
		}
		else {
			ret = sizeof(val);
			*offp += ret;
		}
	}
	return ret;
}

ssize_t cdev_milkpmp_write(struct file * filp, const char __user *buff, size_t count, loff_t *offp)
{
	int val = 0;
	ssize_t ret = count;
	unsigned long flags;

	printk(KERN_DEBUG "coffee2dev->cdev_milkpmp_write: write by \"%s\" (pid %i), count=%lud\n",
		current->comm,
		current->pid,
		(unsigned long)count);

	if (count == sizeof(val)) {
		if (copy_from_user(&val, buff, count)) {
			ret = -EFAULT;
		}
		else {
			if (val > 255) {
				val = 255;
			}
			if (val < 0) {
				val = 0;
			}
			printk(KERN_DEBUG "coffee2dev->cdev_milkpmp_write: value is %d\n", val);
			atomic_set(&this->cdev_milkpmp_val, val);
			local_irq_save(flags);
			SSDR_P1 = val;
			local_irq_restore(flags);
		}
	}
	else {
		ret = -EINVAL;
	}
	return ret;
}

int cdev_milkpmp_release(struct inode * inode_p, struct file * file_p) 
{
	printk(KERN_DEBUG "coffee2dev->cdev_milkpmp_release: released by \"%s\" (pid %i) on \"%s\"\n",
		current->comm,
		current->pid,
		file_p->f_path.dentry->d_name.name);
	return 0;
}

/* -- file operations for cdev_flshmtr */
int cdev_flshmtr_open(struct inode * inode_p, struct file * file_p) 
{
	int ret = 0;
	
	printk(KERN_DEBUG "coffee2dev->cdev_flshmtr_open: opened by \"%s\" (pid %i) on \"%s\"\n",
		current->comm,
		current->pid,
		file_p->f_path.dentry->d_name.name);
	
	ret = nonseekable_open(inode_p, file_p);
	return ret;
}

ssize_t cdev_flshmtr_read(struct file * filp, char __user *buff, size_t count, loff_t *offp)
{
	ssize_t ret = 0;

	printk(KERN_DEBUG "coffee2dev->cdev_flshmtr_read: read by \"%s\" (pid %i)\n",
		current->comm,
		current->pid);
	
	if (*offp == 0 && count >= sizeof(int)) {
		int val = atomic_read(&this->cdev_flshmtr_val);
		if (copy_to_user(buff, &val, sizeof(val))) {
			ret = -EFAULT;
		}
		else {
			ret = sizeof(val);
			*offp += ret;
		}
	}
	return ret;
}

ssize_t cdev_flshmtr_write(struct file * filp, const char __user *buff, size_t count, loff_t *offp)
{
	int val = 0;
	ssize_t ret = count;

	printk(KERN_DEBUG "coffee2dev->cdev_flshmtr_write: write by \"%s\" (pid %i), count=%lud\n",
		current->comm,
		current->pid,
		(unsigned long)count);

	if (count == sizeof(val)) {
		if (copy_from_user(&val, buff, count)) {
			ret = -EFAULT;
		}
		else {
			atomic_set(&this->cdev_flshmtr_val, val);
			*offp += count;
		}
	}
	else {
		ret = -EINVAL;
	}
	return ret;
}

int cdev_flshmtr_release(struct inode * inode_p, struct file * file_p) 
{
	printk(KERN_DEBUG "coffee2dev->cdev_flshmtr_release: released by \"%s\" (pid %i) on \"%s\"\n",
		current->comm,
		current->pid,
		file_p->f_path.dentry->d_name.name);
	return 0;
}

/* -- file operations for cdev_flshpmp */
int cdev_flshpmp_open(struct inode * inode_p, struct file * file_p) 
{
	int ret = 0;
	
	printk(KERN_DEBUG "coffee2dev->cdev_flshpmp_open: opened by \"%s\" (pid %i) on \"%s\"\n",
		current->comm,
		current->pid,
		file_p->f_path.dentry->d_name.name);
	
	ret = nonseekable_open(inode_p, file_p);
	return ret;
}

ssize_t cdev_flshpmp_read(struct file * filp, char __user *buff, size_t count, loff_t *offp)
{
	ssize_t ret = 0;

	printk(KERN_DEBUG "coffee2dev->cdev_flshpmp_read: read by \"%s\" (pid %i)\n",
		current->comm,
		current->pid);
	
	if (*offp == 0 && count >= sizeof(int)) {
		int val = atomic_read(&this->cdev_flshpmp_val);
		if (copy_to_user(buff, &val, sizeof(val))) {
			ret = -EFAULT;
		}
		else {
			ret = sizeof(val);
			*offp += ret;
		}
	}
	return ret;
}

ssize_t cdev_flshpmp_write(struct file * filp, const char __user *buff, size_t count, loff_t *offp)
{
	int val = 0;
	ssize_t ret = count;

	printk(KERN_DEBUG "coffee2dev->cdev_flshpmp_write: write by \"%s\" (pid %i), count=%lud\n",
		current->comm,
		current->pid,
		(unsigned long)count);

	if (count == sizeof(val)) {
		if (copy_from_user(&val, buff, count)) {
			ret = -EFAULT;
		}
		else {
			atomic_set(&this->cdev_flshpmp_val, val);
			*offp += count;
		}
	}
	else {
		ret = -EINVAL;
	}
	return ret;
}

int cdev_flshpmp_release(struct inode * inode_p, struct file * file_p) 
{
	printk(KERN_DEBUG "coffee2dev->cdev_flshpmp_release: released by \"%s\" (pid %i) on \"%s\"\n",
		current->comm,
		current->pid,
		file_p->f_path.dentry->d_name.name);
	return 0;
}

/* -- file operations for cdev_nvaumtr */
int cdev_nvaumtr_open(struct inode * inode_p, struct file * file_p) 
{
	int ret = 0;
	
	printk(KERN_DEBUG "coffee2dev->cdev_nvaumtr_open: opened by \"%s\" (pid %i) on \"%s\"\n",
		current->comm,
		current->pid,
		file_p->f_path.dentry->d_name.name);
	
	ret = nonseekable_open(inode_p, file_p);
	return ret;
}

ssize_t cdev_nvaumtr_read(struct file * filp, char __user *buff, size_t count, loff_t *offp)
{
	ssize_t ret = 0;

	printk(KERN_DEBUG "coffee2dev->cdev_nvaumtr_read: read by \"%s\" (pid %i)\n",
		current->comm,
		current->pid);
	
	if (*offp == 0 && count >= sizeof(int)) {
		int val = atomic_read(&this->cdev_nvaumtr_val);
		if (copy_to_user(buff, &val, sizeof(val))) {
			ret = -EFAULT;
		}
		else {
			ret = sizeof(val);
			//*offp += ret;
		}
	}
	return ret;
}

ssize_t cdev_nvaumtr_write(struct file * filp, const char __user *buff, size_t count, loff_t *offp)
{
	int val = 0;
	ssize_t ret = count;

	printk(KERN_DEBUG "coffee2dev->cdev_nvaumtr_write: write by \"%s\" (pid %i), count=%lud\n",
		current->comm,
		current->pid,
		(unsigned long)count);

	if (count == sizeof(val)) {
		if (copy_from_user(&val, buff, count)) {
			ret = -EFAULT;
		}
		else {
			atomic_set(&this->cdev_nvaumtr_val, val);
			*offp += count;
		}
	}
	else {
		ret = -EINVAL;
	}
	return ret;
}

int cdev_nvaumtr_release(struct inode * inode_p, struct file * file_p) 
{
	printk(KERN_DEBUG "coffee2dev->cdev_nvaumtr_release: released by \"%s\" (pid %i) on \"%s\"\n",
		current->comm,
		current->pid,
		file_p->f_path.dentry->d_name.name);
	return 0;
}

#ifdef USE_TASKLET
/* -- tasklet handler index */
void tasklet_handler_index(unsigned long data)
{
	struct coffee2dev_t *this = (struct coffee2dev_t *)data;
	GPSR(GPIO_TRIGGER) = GPIO_bit(GPIO_TRIGGER);
	GPCR(GPIO_TRIGGER) = GPIO_bit(GPIO_TRIGGER);
	//printk(KERN_DEBUG "tasklet_handler_index called\n");
}
#endif

#ifdef USE_WORKQUEUE
void work_handler_index(struct work_struct *work)
{
	GPSR(GPIO_TRIGGER) = GPIO_bit(GPIO_TRIGGER);
	GPCR(GPIO_TRIGGER) = GPIO_bit(GPIO_TRIGGER);
}
#endif

/* -- IRQ handler index --*/
irqreturn_t index_irq_handler(int irq, void *dev_id)
{
#ifdef USE_DIRECT
	/* -- low level GPIO should be faster */ 
	GPSR(GPIO_TRIGGER) = GPIO_bit(GPIO_TRIGGER);
	GPCR(GPIO_TRIGGER) = GPIO_bit(GPIO_TRIGGER);
	/* -- slower but no visible difference */
	//gpio_set_value(GPIO_TRIGGER, 1);
	//gpio_set_value(GPIO_TRIGGER, 0);
#elif defined(USE_TASKLET)
	tasklet_schedule(&this->tasklet_index);
#elif defined(USE_WORKQUEUE)
	schedule_work(&this->work_index);
#endif
	return IRQ_HANDLED;
}

/* -- IRQ handler button --*/
irqreturn_t button_irq_handler(int irq, void *dev_id)
{
	struct coffee2dev_t *this = dev_id;
	
#ifdef ORCHID
	/* -- schedule tasklet according to irq */
	if (irq == gpio_to_irq(GPIO_S5)) {
		/* -- schedule */
		tasklet_schedule(&this->tasklet_S5);
	}
	else if (irq == gpio_to_irq(GPIO_S6)) {
		/* -- schedule */
		tasklet_schedule(&this->tasklet_S6);
	}
	else if (irq == gpio_to_irq(GPIO_S7)) {
		/* -- schedule */
		tasklet_schedule(&this->tasklet_S7);
	}
	else if (irq == gpio_to_irq(GPIO_S8)) {
		/* -- schedule */
		tasklet_schedule(&this->tasklet_S7);
	}
#elif defined(CARME)
	/* -- schedule tasklet according to irq */
	if (irq == gpio_to_irq(GPIO_T0)) {		
		printk(KERN_DEBUG "button_irq_handler called for GPIO_T0 / irq %d\n", irq);
		button_enqueue(this->button_kfifo, VAL_BTN_08_T0);
	}
	else if (irq == gpio_to_irq(GPIO_T1)) {
		printk(KERN_DEBUG "button_irq_handler called for GPIO_T1 / irq %d\n", irq);
		button_enqueue(this->button_kfifo, VAL_BTN_09_T1);
	}
	else if (irq == gpio_to_irq(GPIO_T2)) {
		printk(KERN_DEBUG "button_irq_handler called for GPIO_T2 / irq %d\n", irq);
		button_enqueue(this->button_kfifo, VAL_BTN_10_T2);
	}
	else if (irq == gpio_to_irq(GPIO_T3)) {
		printk(KERN_DEBUG "button_irq_handler called for GPIO_T3 / irq %d\n", irq);
		button_enqueue(this->button_kfifo, VAL_BTN_11_T3);
	}
#endif	
	//printk(KERN_DEBUG "button_irq_handler called for irq %d\n", irq);
	return IRQ_HANDLED;
}

#ifdef ORCHID

/* -- tasklet handlers */

void tasklet_handler_S5(unsigned long data)
{
	struct coffee2dev_t *this = (struct coffee2dev_t *)data;
	printk(KERN_DEBUG "tasklet_handler_S5 called\n");
	atomic_set(&this->cdev_buttons_val, 5);
}

void tasklet_handler_S6(unsigned long data)
{
	struct coffee2dev_t *this = (struct coffee2dev_t *)data;
	printk(KERN_DEBUG "tasklet_handler_S6 called\n");
	atomic_set(&this->cdev_buttons_val, 6);
}

void tasklet_handler_S7(unsigned long data)
{
	struct coffee2dev_t *this = (struct coffee2dev_t *)data;
	printk(KERN_DEBUG "tasklet_handler_S7 called\n");
	atomic_set(&this->cdev_buttons_val, 7);
}

void tasklet_handler_S8(unsigned long data)
{
	struct coffee2dev_t *this = (struct coffee2dev_t *)data;
	printk(KERN_DEBUG "tasklet_handler_S8 called\n");
	atomic_set(&this->cdev_buttons_val, 8);
}

#elif defined(CARME)

/* -- timer related functions */

void timer_function(unsigned long arg)
{	
	/* -- get "this" pointer */
	struct coffee2dev_t * this = (struct coffee2dev_t *)arg;
	
	/* -- read switches */
	u8 switchval = ioread8(this->io_base + SWITCH_OFFSET);
	if (switchval != this->switch_shadow_reg) {
		/* -- add changed switches to button kfifo */
		if ((switchval & 1) != (this->switch_shadow_reg & 1)) {
			printk(KERN_DEBUG "detected switch S0\n");
			button_enqueue(this->button_kfifo, VAL_BTN_00_S0);		
		}
		if ((switchval & 2) != (this->switch_shadow_reg & 2)) {
			printk(KERN_DEBUG "detected switch S1\n");	
			button_enqueue(this->button_kfifo, VAL_BTN_01_S1);		
		}
		if ((switchval & 4) != (this->switch_shadow_reg & 4)) {
			printk(KERN_DEBUG "detected switch S2\n");	
			button_enqueue(this->button_kfifo, VAL_BTN_02_S2);
		}
		if ((switchval & 8) != (this->switch_shadow_reg & 8)) {
			printk(KERN_DEBUG "detected switch S3\n");	
			button_enqueue(this->button_kfifo, VAL_BTN_03_S3);
		}
		if ((switchval & 16) != (this->switch_shadow_reg & 16)) {
			printk(KERN_DEBUG "detected switch S4\n");	
			button_enqueue(this->button_kfifo, VAL_BTN_04_S4);
		}
		if ((switchval & 32) != (this->switch_shadow_reg & 32)) {
			printk(KERN_DEBUG "detected switch S5\n");
			button_enqueue(this->button_kfifo, VAL_BTN_05_S5);
		}
		if ((switchval & 64) != (this->switch_shadow_reg & 64)) {
			printk(KERN_DEBUG "detected switch S6\n");	
			button_enqueue(this->button_kfifo, VAL_BTN_06_S6);
		}
		if ((switchval & 128) != (this->switch_shadow_reg & 128)) {
			printk(KERN_DEBUG "detected switch S7\n");	
			button_enqueue(this->button_kfifo, VAL_BTN_07_S7);
		}		
		this->switch_shadow_reg = switchval;
	}		
	/* -- rearm timer */
	timer_arm(&this->timer, TIMER_INTERVAL);	
}

void timer_arm(struct timer_list *timer, int interval)
{
	unsigned long j = jiffies;
	timer->expires = j + interval;
	add_timer(timer);
}

/* -- button kfifo related functions */

void button_enqueue(struct kfifo *button_kfifo, unsigned char button)
{
	/* -- if queue is full input gets lost */
	if (kfifo_len(this->button_kfifo) < BUTTON_KFIFO_LENGTH)
	{
		/* -- put the button in the fifo */
		kfifo_put(button_kfifo, &button, sizeof(button));		
	}
	/* -- wake up waiting readers */
	wake_up_interruptible(&this->button_kfifo_wait);
}

int button_dequeue_wait(struct kfifo *button_kfifo, unsigned char *button)
{
	int ret;
	/* -- try to read a button */
	while ((ret = kfifo_get(button_kfifo, button, sizeof(*button))) == 0)
	{
		/* -- wait until data is available */
		DEFINE_WAIT(wait);
		prepare_to_wait(&this->button_kfifo_wait, &wait, TASK_INTERRUPTIBLE);
		finish_wait(&this->button_kfifo_wait, &wait);
		if (signal_pending(current)) {		
		    ret = -ERESTARTSYS;
		    break;
		}
	}
	/* -- return bytes read */
	return ret;
}

int button_dequeue_nowait(struct kfifo *button_kfifo, unsigned char *button)
{	
	/* -- try to read a button, return bytes read */
	return kfifo_get(button_kfifo, button, sizeof(*button));
}

#endif	

#ifdef ORCHID

int init_gpio_led(int gpio, char *label)
{
	int ret = 0;
	/* GPIO LED init */
	if (gpio_request(gpio, label) != 0) {
		printk(KERN_WARNING "coffee2dev_init: Error requesting gpio %d!\n", gpio);
		ret = -1;
	}
	else if (pxa_gpio_mode(gpio | GPIO_OUT | GPIO_DFLT_LOW) != 0) {
		printk(KERN_WARNING "coffee2dev_init: Error setting direction for gpio %d!\n", gpio);
		ret = -1;
	}
	return ret;
}

#endif


int init_gpio_button(int gpio, char *label)
{
	int ret = 0;
	/* -- GPIO button init */
	if (gpio_request(gpio, label) != 0) {
		printk(KERN_WARNING "coffee2dev_init: Error requesting gpio %d!\n", gpio);
		ret = -1;
	}
	if (pxa_gpio_mode(gpio | GPIO_IN) != 0) {
		printk(KERN_WARNING "coffee2dev_init: Error setting direction for gpio %d!\n", gpio);
		ret = -1;
	}
	return ret;
}

int init_gpio_pwm(int gpio_speed, char *label_speed, int gpio_direction, char *label_direction)
{
	int ret = 0;
	/* -- GPIO speed */
	if (gpio_request( gpio_speed, label_speed) != 0) {
		printk(KERN_WARNING "init_gpio_pwm: Error requesting gpio %d!\n",  gpio_speed);
		ret = -1;
	}
	if (pxa_gpio_mode(gpio_speed | GPIO_OUT | GPIO_ALT_FN_2_OUT) != 0) {
		printk(KERN_WARNING "init_gpio_pwm: Error setting alternate function 2 out for gpio %d!\n", gpio_speed);
		ret = -1;
	}
	/* -- GPIO direction */
	if (gpio_request( gpio_direction, label_direction) != 0) {
		printk(KERN_WARNING "init_gpio_pwm: Error requesting gpio %d!\n",  gpio_direction);
		ret = -1;
	}
	if (pxa_gpio_mode(gpio_direction | GPIO_OUT | GPIO_DFLT_LOW) != 0) {
		printk(KERN_WARNING "init_gpio_pwm: Error setting direction for gpio %d!\n", gpio_direction);
		ret = -1;
	}
	return ret;
}

int init_gpio(int gpio, char *label, int flags)
{
	int ret = 0;
	/* -- GPIO request */
	if (gpio_request(gpio, label) != 0) {
		printk(KERN_WARNING "init_gpio: Error requesting gpio %d!\n", gpio);
		ret = -1;
	}
	/* -- GPIO mode */
	if (pxa_gpio_mode(gpio | flags) != 0) {
		printk(KERN_WARNING "init_gpio: Error setting mode for gpio %d!\n", gpio);
		ret = -1;
	}
	return ret;
}

int __init coffee2dev_init(void) {
	
	int ret = 0;
	dev_t dev = 0;
	unsigned long flags;
	
	/* -- kzalloc this */
	this = kzalloc(sizeof(struct coffee2dev_t), GFP_KERNEL);
	if (this == NULL) {
		printk(KERN_WARNING "coffee2dev_init: kzalloc for \"this\" failed!\n");
		ret = -ENOMEM;
		goto fail;
	}

	/* -- initialize atomic values */ 
	atomic_set(&this->cdev_service_val, -1);
	atomic_set(&this->cdev_display_val, -1);
	atomic_set(&this->cdev_buttons_val, -1);
	atomic_set(&this->cdev_cakemtr_val, -1);
	atomic_set(&this->cdev_cakermv_val, -1);
	atomic_set(&this->cdev_pwdrmtr_val, -1);
	atomic_set(&this->cdev_coffgrn_val, -1);
	atomic_set(&this->cdev_coffdfd_val, -1);
	atomic_set(&this->cdev_watrtmp_val, -1);
	atomic_set(&this->cdev_watrhtr_val, -1);
	atomic_set(&this->cdev_watrmtr_val, -1);
	atomic_set(&this->cdev_watrpmp_val, -1);
	atomic_set(&this->cdev_milkmtr_val, -1);
	atomic_set(&this->cdev_milkpmp_val, -1);
	atomic_set(&this->cdev_flshmtr_val, -1);
	atomic_set(&this->cdev_flshpmp_val, -1);
	atomic_set(&this->cdev_nvaumtr_val, -1);
	atomic_set(&this->led_shadow_reg, 0);
	
	/* -- initialize device resources */ 
	if ((ret = alloc_chrdev_region(&dev, 0, 17, "coffee2dev") < 0)) {
		printk(KERN_WARNING "coffee2dev_init: can't get major!\n");
		goto fail;
	}
	
	cdev_init(&this->cdev_service, &fops_cdev_service);
	this->cdev_service.owner = THIS_MODULE;
	
	cdev_init(&this->cdev_display, &fops_cdev_display);
	this->cdev_display.owner = THIS_MODULE;
	
	cdev_init(&this->cdev_buttons, &fops_cdev_buttons);
	this->cdev_buttons.owner = THIS_MODULE;
	
	cdev_init(&this->cdev_cakemtr, &fops_cdev_cakemtr);
	this->cdev_cakemtr.owner = THIS_MODULE;
	
	cdev_init(&this->cdev_cakermv, &fops_cdev_cakermv);
	this->cdev_cakermv.owner = THIS_MODULE;
	
	cdev_init(&this->cdev_pwdrmtr, &fops_cdev_pwdrmtr);
	this->cdev_pwdrmtr.owner = THIS_MODULE;
	
	cdev_init(&this->cdev_coffgrn, &fops_cdev_coffgrn);
	this->cdev_coffgrn.owner = THIS_MODULE;
	
	cdev_init(&this->cdev_coffdfd, &fops_cdev_coffdfd);
	this->cdev_coffdfd.owner = THIS_MODULE;
	
	cdev_init(&this->cdev_watrtmp, &fops_cdev_watrtmp);
	this->cdev_watrtmp.owner = THIS_MODULE;
	
	cdev_init(&this->cdev_watrhtr, &fops_cdev_watrhtr);
	this->cdev_watrhtr.owner = THIS_MODULE;
	
	cdev_init(&this->cdev_watrmtr, &fops_cdev_watrmtr);
	this->cdev_watrmtr.owner = THIS_MODULE;
	
	cdev_init(&this->cdev_watrpmp, &fops_cdev_watrpmp);
	this->cdev_watrpmp.owner = THIS_MODULE;
	
	cdev_init(&this->cdev_milkmtr, &fops_cdev_milkmtr);
	this->cdev_milkmtr.owner = THIS_MODULE;
	
	cdev_init(&this->cdev_milkpmp, &fops_cdev_milkpmp);
	this->cdev_milkpmp.owner = THIS_MODULE;
	
	cdev_init(&this->cdev_flshmtr, &fops_cdev_flshmtr);
	this->cdev_flshmtr.owner = THIS_MODULE;
	
	cdev_init(&this->cdev_flshpmp, &fops_cdev_flshpmp);
	this->cdev_flshpmp.owner = THIS_MODULE;
	
	cdev_init(&this->cdev_nvaumtr, &fops_cdev_nvaumtr);
	this->cdev_nvaumtr.owner = THIS_MODULE;
	
	if ((ret = cdev_add(&this->cdev_service, dev, 1)) < 0) {
		printk(KERN_NOTICE "coffee2dev_init: Error %d adding cdev_service!\n", ret);
		goto fail;
	}
	
	if ((ret = cdev_add(&this->cdev_display, dev + 1, 1)) < 0) {
		printk(KERN_NOTICE "coffee2dev_init: Error %d adding cdev_display!\n", ret);
		goto fail;
	}
	
	if ((ret = cdev_add(&this->cdev_buttons, dev + 2, 1)) < 0) {
		printk(KERN_NOTICE "coffee2dev_init: Error %d adding cdev_buttons!\n", ret);
		goto fail;
	}
	
	if ((ret = cdev_add(&this->cdev_cakemtr, dev + 3, 1)) < 0) {
		printk(KERN_NOTICE "coffee2dev_init: Error %d adding cdev_cakemtr!\n", ret);
		goto fail;
	}
	
	if ((ret = cdev_add(&this->cdev_cakermv, dev + 4, 1)) < 0) {
		printk(KERN_NOTICE "coffee2dev_init: Error %d adding cdev_cakermv!\n", ret);
		goto fail;
	}
	
	if ((ret = cdev_add(&this->cdev_pwdrmtr, dev + 5, 1)) < 0) {
		printk(KERN_NOTICE "coffee2dev_init: Error %d adding cdev_pwdrmtr!\n", ret);
		goto fail;
	}
	
	if ((ret = cdev_add(&this->cdev_coffgrn, dev + 6, 1)) < 0) {
		printk(KERN_NOTICE "coffee2dev_init: Error %d adding cdev_coffgrn!\n", ret);
		goto fail;
	}
	
	if ((ret = cdev_add(&this->cdev_coffdfd, dev + 7, 1)) < 0) {
		printk(KERN_NOTICE "coffee2dev_init: Error %d adding cdev_coffdfd!\n", ret);
		goto fail;
	}
	
	if ((ret = cdev_add(&this->cdev_watrtmp, dev + 8, 1)) < 0) {
		printk(KERN_NOTICE "coffee2dev_init: Error %d adding cdev_watrtmp!\n", ret);
		goto fail;
	}
	
	if ((ret = cdev_add(&this->cdev_watrhtr, dev + 9, 1)) < 0) {
		printk(KERN_NOTICE "coffee2dev_init: Error %d adding cdev_watrhtr!\n", ret);
		goto fail;
	}
	
	if ((ret = cdev_add(&this->cdev_watrmtr, dev + 10, 1)) < 0) {
		printk(KERN_NOTICE "coffee2dev_init: Error %d adding cdev_watrmtr!\n", ret);
		goto fail;
	}
	
	if ((ret = cdev_add(&this->cdev_watrpmp, dev + 11, 1)) < 0) {
		printk(KERN_NOTICE "coffee2dev_init: Error %d adding cdev_watrpmp!\n", ret);
		goto fail;
	}
	
	if ((ret = cdev_add(&this->cdev_milkmtr, dev + 12, 1)) < 0) {
		printk(KERN_NOTICE "coffee2dev_init: Error %d adding cdev_milkmtr!\n", ret);
		goto fail;
	}
	
	if ((ret = cdev_add(&this->cdev_milkpmp, dev + 13, 1)) < 0) {
		printk(KERN_NOTICE "coffee2dev_init: Error %d adding cdev_milkpmp!\n", ret);
		goto fail;
	}
	
	if ((ret = cdev_add(&this->cdev_flshmtr, dev + 14, 1)) < 0) {
		printk(KERN_NOTICE "coffee2dev_init: Error %d adding cdev_flshmtr!\n", ret);
		goto fail;
	}
	
	if ((ret = cdev_add(&this->cdev_flshpmp, dev + 15, 1)) < 0) {
		printk(KERN_NOTICE "coffee2dev_init: Error %d adding cdev_flshpmp!\n", ret);
		goto fail;
	}
	
	if ((ret = cdev_add(&this->cdev_nvaumtr, dev + 16, 1)) < 0) {
		printk(KERN_NOTICE "coffee2dev_init: Error %d adding cdev_nvaumtr!\n", ret);
		goto fail;
	}
	
#ifdef ORCHID
	/* GPIO LED init V1 */
	if (init_gpio_led(GPIO_V1, "V1")) {
		goto fail;
	}
	/* GPIO LED init V2 */
	if (init_gpio_led(GPIO_V2, "V2")) {
		goto fail;
	}
	/* GPIO LED init V3 */
	if (init_gpio_led(GPIO_V3, "V3")) {
		goto fail;
	}
	/* GPIO LED init V4 */
	if (init_gpio_led(GPIO_V4, "V4")) {
		goto fail;
	}
	/* GPIO LED init V5 */
	if (init_gpio_led(GPIO_V5, "V5")) {
		goto fail;
	}
	/* GPIO LED init V6 */
	if (init_gpio_led(GPIO_V6, "V6")) {
		goto fail;
	}
	/* GPIO LED init V7 */
	if (init_gpio_led(GPIO_V7, "V7")) {
		goto fail;
	}
	/* GPIO LED init V8 */
	if (init_gpio_led(GPIO_V8, "V8")) {
		goto fail;
	}
	
	/* -- reset leds */
	gpio_set_value(GPIO_V1, 0);
	gpio_set_value(GPIO_V2, 0);
	gpio_set_value(GPIO_V3, 0);
	gpio_set_value(GPIO_V4, 0);
	gpio_set_value(GPIO_V5, 0);
	gpio_set_value(GPIO_V6, 0);
	gpio_set_value(GPIO_V7, 0);
	gpio_set_value(GPIO_V8, 0);
	
	/* -- set GPIO 118 to high */
	if (gpio_request(GPIO_SELECTOUTPUT, "SELECTOUTPUT") != 0) {
		printk(KERN_WARNING "coffee2dev_init: Error requesting gpio %d!\n", GPIO_SELECTOUTPUT);
		goto fail;
	}
	if (pxa_gpio_mode(GPIO_SELECTOUTPUT | GPIO_OUT | GPIO_DFLT_HIGH) != 0) {
		printk(KERN_WARNING "coffee2dev_init: Error setting direction for gpio %d!\n", GPIO_SELECTOUTPUT);
		goto fail;
	}
	
	/* -- GPIO button init */
	if (init_gpio_button(GPIO_S5, "S5") != 0) {
		goto fail;
	}
	if (init_gpio_button(GPIO_S6, "S6") != 0) {
		goto fail;
	}
	if (init_gpio_button(GPIO_S7, "S7") != 0) {
		goto fail;
	}
	if (init_gpio_button(GPIO_S8, "S8") != 0) {
		goto fail;
	}
	
	/* -- set GPIO 117 to low */
	if (gpio_request(GPIO_SELECTINPUT, "SELECTINPUT") != 0) {
		printk(KERN_WARNING "coffee2dev_init: Error requesting gpio %d!\n", GPIO_SELECTINPUT);
		goto fail;
	}
	if (pxa_gpio_mode(GPIO_SELECTINPUT | GPIO_OUT | GPIO_DFLT_LOW) != 0)	{
		printk(KERN_WARNING "coffee2dev_init: Error setting direction for gpio %d!\n", GPIO_SELECTINPUT);
		goto fail;
	}	
	
	/* -- tasklet initialization */
	tasklet_init(&this->tasklet_S5, tasklet_handler_S5, (unsigned long)this);
	tasklet_init(&this->tasklet_S6, tasklet_handler_S6, (unsigned long)this);
	tasklet_init(&this->tasklet_S7, tasklet_handler_S7, (unsigned long)this);
	tasklet_init(&this->tasklet_S8, tasklet_handler_S8, (unsigned long)this);
	
	/* -- install interrupt handler */
	if (request_irq(gpio_to_irq(GPIO_S5), button_irq_handler, IRQF_TRIGGER_RISING, "coffee2dev", this) != 0) {
		printk(KERN_WARNING "coffee2dev_init: Error installing irq handler %d\n", GPIO_S5);
		goto fail;
	}
	if (request_irq(gpio_to_irq(GPIO_S6), button_irq_handler, IRQF_TRIGGER_RISING, "coffee2dev", this) != 0) {
		printk(KERN_WARNING "coffee2dev_init: Error installing irq handler %d\n", GPIO_S6);
		goto fail;
	}
	if (request_irq(gpio_to_irq(GPIO_S7), button_irq_handler, IRQF_TRIGGER_RISING, "coffee2dev", this) != 0) {
		printk(KERN_WARNING "coffee2dev_init: Error installing irq handler %d\n", GPIO_S7);
		goto fail;
	}
	if (request_irq(gpio_to_irq(GPIO_S8), button_irq_handler, IRQF_TRIGGER_RISING, "coffee2dev", this) != 0) {
		printk(KERN_WARNING "coffee2dev_init: Error installing irq handler %d\n", GPIO_S8);
		goto fail;
	}

#elif defined(CARME)

	/* -- setup memory mapping for LEDs and potentially other resources */
	this->mem_res = request_mem_region(IO_BASE, MAP_SIZE, "coffee2dev_carme");
	if (this->mem_res == NULL)	{
		printk(KERN_WARNING "coffee2dev_init: request_mem_region failed\n");
		goto fail;
	}
	this->io_base = ioremap_nocache(IO_BASE, MAP_SIZE);
	if (this->io_base == NULL) {
		printk(KERN_WARNING "coffee2dev_init: ioremap_nocache failed\n");
		goto fail;
	}
	
	/* -- reset leds */
	iowrite8(0, this->io_base + LED_OFFSET);
	
	/* -- GPIO button init */
	if (init_gpio_button(GPIO_T0, LABL_T0) != 0) {
		printk(KERN_WARNING "coffee2dev_init: init_gpio_button failed for GPIO %d / %s\n", GPIO_T0, LABL_T0);
		goto fail;
	}
	if (init_gpio_button(GPIO_T1, LABL_T1) != 0) {
		printk(KERN_WARNING "coffee2dev_init: init_gpio_button failed for GPIO %d / %s\n", GPIO_T1, LABL_T1);
		goto fail;
	}
	if (init_gpio_button(GPIO_T2, LABL_T2) != 0) {
		printk(KERN_WARNING "coffee2dev_init: init_gpio_button failed for GPIO %d / %s\n", GPIO_T2, LABL_T2);
		goto fail;
	}
	if (init_gpio_button(GPIO_T3, LABL_T3) != 0) {
		printk(KERN_WARNING "coffee2dev_init: init_gpio_button failed for GPIO %d / %s\n", GPIO_T3, LABL_T3);
		goto fail;
	}
#ifdef USE_TASKLET	
	/* -- tasklet initialization */
	tasklet_init(&this->tasklet_index, tasklet_handler_index, (unsigned long)this);
#elif defined(USE_WORKQUEUE)
	INIT_WORK(&this->work_index, work_handler_index);
#endif	

	/* -- install button interrupt handlers */
	if (request_irq(gpio_to_irq(GPIO_T0), button_irq_handler, IRQF_TRIGGER_RISING, "coffee2dev", this) != 0) {
		printk(KERN_WARNING "coffee2dev_init: Error installing irq handler %d\n", GPIO_T0);
		goto fail;
	}
	if (request_irq(gpio_to_irq(GPIO_T1), button_irq_handler, IRQF_TRIGGER_RISING, "coffee2dev", this) != 0) {
		printk(KERN_WARNING "coffee2dev_init: Error installing irq handler %d\n", GPIO_T1);
		goto fail;
	}
	if (request_irq(gpio_to_irq(GPIO_T2), button_irq_handler, IRQF_TRIGGER_RISING, "coffee2dev", this) != 0) {
		printk(KERN_WARNING "coffee2dev_init: Error installing irq handler %d\n", GPIO_T2);
		goto fail;
	}
	if (request_irq(gpio_to_irq(GPIO_T3), button_irq_handler, IRQF_TRIGGER_RISING, "coffee2dev", this) != 0) {
		printk(KERN_WARNING "coffee2dev_init: Error installing irq handler %d\n", GPIO_T3);
		goto fail;
	}
	
	/* -- PWM3 */
	if (init_gpio_pwm(GPIO_PWM3_SPEED, LABL_PWM3_SPEED, GPIO_PWM3_DIRECTION, LABL_PWM3_DIRECTION) != 0) {
		printk(KERN_WARNING "coffee2dev_init: init_gpio_button failed for GPIO %d / %s, %d / %s\n", 
						GPIO_PWM3_SPEED, LABL_PWM3_SPEED, GPIO_PWM3_DIRECTION, LABL_PWM3_DIRECTION);
		goto fail;
	}
	
	PWMCR3 = 0x14; 	/* divide 13MHz clock by 0x14 */
	PWMDCR3 = 0;   	/* initial speed is zero */
	PWMPCR3 = 100; 	/* periode is 100 time ticks */
	CKEN |= 0x03;	/* enable clock for PWM3 */
	
	/* -- strobo */
	if (init_gpio(GPIO_SSPSCLK, LABL_SSPSCLK, GPIO_OUT | GPIO_ALT_FN_2_OUT) != 0)
	{
		goto fail;
	}
	if (init_gpio(GPIO_SSPSFRM, LABL_SSPSFRM, GPIO_OUT | GPIO_ALT_FN_2_OUT) != 0)
	{
		goto fail;
	}
	if (init_gpio(GPIO_SSPTxD, LABL_SSPTxD, GPIO_OUT | GPIO_ALT_FN_2_OUT) != 0)
	{
		goto fail;
	}
	if (init_gpio(GPIO_SSPRxD, LABL_SSPRxD, GPIO_IN | GPIO_ALT_FN_1_IN) != 0)
	{
		goto fail;
	}
	if (init_gpio(GPIO_SPI_CS, LABL_SPI_CS, GPIO_OUT | GPIO_DFLT_HIGH) != 0)
	{
		goto fail;
	}
	if (init_gpio(GPIO_INDEX, LABL_INDEX, GPIO_IN) != 0)
	{
		goto fail;
	}
	if (init_gpio(GPIO_TRIGGER, LABL_TRIGGER, GPIO_OUT | GPIO_DFLT_LOW) != 0)
	{
		goto fail;
	}
	
	/* -- irq handler for index */
	if (request_irq(gpio_to_irq(GPIO_INDEX), index_irq_handler, IRQF_TRIGGER_RISING, "coffee2dev", this) != 0) {
		printk(KERN_WARNING "coffee2dev_init: Error installing irq handler %d\n", GPIO_INDEX);
		goto fail;
	}
	
	/* -- setup SPI for strobo */
	local_irq_save(flags);
	
	SSCR0_P1 &= ~SSCR0_SSE;           // disable synchronous serial
	SSCR0_P1 = SSCR0_DataSize(8);     // data size is 8 bit
	SSCR1_P1 = SSCR1_SPH | SSCR1_SPO; // Motorola SPI
	CKEN |= (1 << CKEN_SSP1);         // enable SSP1 Unit Clock
	SSCR0_P1 |= SSCR0_SSE;            // enable synchronous serial
	
	SSDR_P1 = 255;
	
	local_irq_restore(flags);
	
	/* -- initialize kfifo buttons spinlock */
	spin_lock_init(&this->button_kfifo_spinlock);	
	/* -- initialize kfifo waitqueue */
	init_waitqueue_head(&this->button_kfifo_wait);
	/* -- setup kfifo for buttons */
	this->button_kfifo = kfifo_alloc(BUTTON_KFIFO_LENGTH, GFP_KERNEL, &this->button_kfifo_spinlock);
	if (IS_ERR(this->button_kfifo)) {
		printk(KERN_WARNING "coffee2dev_init: kfifo_alloc failed\n");
		goto fail;
	}	
	
	/* -- setup and arm kernel timer to read switches */
	this->switch_shadow_reg = ioread8(this->io_base + SWITCH_OFFSET);
	this->timer.data = (unsigned long)this;
	this->timer.function = timer_function;
	init_timer(&this->timer);	
	timer_arm(&this->timer, TIMER_INTERVAL);

#endif

	printk(KERN_INFO "coffee2dev loaded\n");
	
	return ret;

fail:
	printk(KERN_INFO "coffee2dev failed to load\n");
	coffee2dev_exit();
	return ret;
}

void __exit coffee2dev_exit(void) {
	if (this != NULL)
	{
		dev_t dev = this->cdev_service.dev;
#ifdef ORCHID
		free_irq(gpio_to_irq(GPIO_S5), this);
		free_irq(gpio_to_irq(GPIO_S6), this);
		free_irq(gpio_to_irq(GPIO_S7), this);
		free_irq(gpio_to_irq(GPIO_S8), this);
		tasklet_kill(&this->tasklet_S5);
		tasklet_kill(&this->tasklet_S6);
		tasklet_kill(&this->tasklet_S7);
		tasklet_kill(&this->tasklet_S8);
		gpio_free(GPIO_V1);
		gpio_free(GPIO_V2);
		gpio_free(GPIO_V3);
		gpio_free(GPIO_V4);
		gpio_free(GPIO_V5);
		gpio_free(GPIO_V6);
		gpio_free(GPIO_V7);
		gpio_free(GPIO_V8);
		gpio_free(GPIO_SELECTOUTPUT);
		gpio_free(GPIO_S5);
		gpio_free(GPIO_S6);
		gpio_free(GPIO_S7);
		gpio_free(GPIO_S8);
		gpio_free(GPIO_SELECTINPUT);
#elif defined(CARME)

#ifdef USE_TASKLET
		tasklet_disable_nosync(&this->tasklet_index);
		tasklet_kill(&this->tasklet_index);
#elif defined(USE_WORKQUEUE)
		cancel_work_sync(&this->work_index);
#endif
		del_timer_sync(&this->timer);
		kfifo_free(this->button_kfifo);
		free_irq(gpio_to_irq(GPIO_T0), this);
		free_irq(gpio_to_irq(GPIO_T1), this);
		free_irq(gpio_to_irq(GPIO_T2), this);
		free_irq(gpio_to_irq(GPIO_T3), this);
		free_irq(gpio_to_irq(GPIO_INDEX), this);
		gpio_free(GPIO_T0);
		gpio_free(GPIO_T1);
		gpio_free(GPIO_T2);
		gpio_free(GPIO_T3);
		gpio_free(GPIO_PWM3_SPEED);
		gpio_free(GPIO_PWM3_DIRECTION);
		gpio_free(GPIO_SSPSCLK);
		gpio_free(GPIO_SSPSFRM);
		gpio_free(GPIO_SSPTxD);
		gpio_free(GPIO_SSPRxD);
		gpio_free(GPIO_SPI_CS);
		gpio_free(GPIO_INDEX);
		gpio_free(GPIO_TRIGGER);
		iowrite8(0, this->io_base + LED_OFFSET);
		iounmap(this->io_base);
		this->io_base = NULL;
		atomic_set(&this->led_shadow_reg, 0);
		release_mem_region(IO_BASE, MAP_SIZE);
		this->mem_res = NULL;
#endif		
		cdev_del(&this->cdev_service);
		cdev_del(&this->cdev_display);
		cdev_del(&this->cdev_buttons);
		cdev_del(&this->cdev_cakemtr);
		cdev_del(&this->cdev_cakermv);
		cdev_del(&this->cdev_pwdrmtr);
		cdev_del(&this->cdev_coffgrn);
		cdev_del(&this->cdev_coffdfd);
		cdev_del(&this->cdev_watrtmp);
		cdev_del(&this->cdev_watrhtr);
		cdev_del(&this->cdev_watrmtr);
		cdev_del(&this->cdev_watrpmp);
		cdev_del(&this->cdev_milkmtr);
		cdev_del(&this->cdev_milkpmp);
		cdev_del(&this->cdev_flshmtr);
		cdev_del(&this->cdev_flshpmp);
		cdev_del(&this->cdev_nvaumtr);
		unregister_chrdev_region(dev, 17);
		kfree(this);
	}
	printk(KERN_INFO "coffee2dev unloaded\n");
}

module_init(coffee2dev_init);
module_exit(coffee2dev_exit);
